#include "pch.h"
#include "SpinningCubeRender.h"
#include "BasicShapes.h"
#include "BasicLoader.h"
#include "Exceptions.h"

using namespace Microsoft::WRL;
using namespace Windows::Foundation;
using namespace Windows::UI::Core;

SpinningCubeRender::SpinningCubeRender()
{
	this->shapes = new ShapeLibrary();
	this->textures = new TextureLibrary();
	this->shaderPairs = new ShaderPairLibrary();
}

void SpinningCubeRender::CreateDeviceIndependentResources()
{
    AbstractRender::CreateDeviceIndependentResources();
}

void SpinningCubeRender::CreateDeviceResources()
{
    AbstractRender::CreateDeviceResources();

    BasicLoader^ loader = ref new BasicLoader(m_d3dDevice.Get());

	// initialize the shadera
	shaderPair = 
		new ShaderPair(
			m_d3dDevice.Get(), 
			L"SimpleVertexShader.cso", 
			L"SimplePixelShader.cso");

	int shaderID = this->shaderPairs->Add(shaderPair);

   
	///////////////////////////////////////////////////////////////////////////
    // initialize the 3d shapes
    BasicShapes^ shapeBuilder = ref new BasicShapes(m_d3dDevice.Get());

	cube = shapeBuilder->CreateBoxModel(0.5f, 2.0f, 0.5f, 0.0f, 0.5f, 0.5f);
	int shapeID = this->shapes->Add(cube);

	////////////////////////////////////////////////////////////////////////////
	// initializ the textures
	this->texture = 
		new Texture(
			m_d3dDevice.Get(),
			L"windowsbig-sdk.png");

	int textureID = this->textures->Add(this->texture);

	///////////////////////////////////////////////////////////////////////////
	// Create the simple model
	cubeModel = new Model();
	cubeModel->ShapeID = shapeID;
	cubeModel->ShaderID = shaderID;
	cubeModel->TextureID = textureID;
	
	///////////////////////////////////////////////////////////////////////////
    // create the constant buffer for updating model and camera data
    CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);
    DX::ThrowIfFailed(
        m_d3dDevice->CreateBuffer(
            &constantBufferDesc,
            nullptr,             // leave the buffer uninitialized
            &m_constantBuffer
            )
        );

    // create the sampler
    D3D11_SAMPLER_DESC samplerDesc;
    ZeroMemory(&samplerDesc, sizeof(D3D11_SAMPLER_DESC));
    samplerDesc.Filter = D3D11_FILTER_ANISOTROPIC;
    samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.MipLODBias = 0.0f;
    // use 4x on feature level 9.2 and above, otherwise use only 2x
    samplerDesc.MaxAnisotropy = m_featureLevel > D3D_FEATURE_LEVEL_9_1 ? 4 : 2;
    samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
    samplerDesc.BorderColor[0] = 0.0f;
    samplerDesc.BorderColor[1] = 0.0f;
    samplerDesc.BorderColor[2] = 0.0f;
    samplerDesc.BorderColor[3] = 0.0f;
    // allow use of all mip levels
    samplerDesc.MinLOD = 0;
    samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

    DX::ThrowIfFailed(
        m_d3dDevice->CreateSamplerState(
            &samplerDesc,
            &m_sampler)
        );

    m_camera = new BasicCamera();
}

void SpinningCubeRender::CreateWindowSizeDependentResources()
{
    AbstractRender::CreateWindowSizeDependentResources();

    // update the 3D projection matrix
    m_camera->SetProjectionParameters(
        70.0f,                                                // use a 70-degree vertical field of view
        m_renderTargetSize.Width / m_renderTargetSize.Height, // specify the aspect ratio of the window
        0.01f,                                                // specify the nearest Z-distance at which to draw vertices
        100.0f                                                // specify the farthest Z-distance at which to draw vertices
        );

    // save the new projection matrix to the constant buffer data struct
    m_camera->GetProjectionMatrix(&m_modelViewProjection.projection);
}

void SpinningCubeRender::Update(float timeTotal, float timeDelta)
{
    // update the model matrix based on the time
    modelTransform = rotationY(-timeTotal * 60.0f);

    // update the view matrix based on the camera position
    // note that for this sample, the camera position is fixed
    m_camera->SetViewParameters(
        float3(0, 1.0f, 3.0f),
        float3(0, 1.0f, 0),
        float3(0, 1, 0)
        );

    m_camera->GetViewMatrix(&m_modelViewProjection.view);

}

void SpinningCubeRender::Render()
{
    m_d3dContext->OMSetRenderTargets(
        1,
        m_d3dRenderTargetView.GetAddressOf(),
        m_d3dDepthStencilView.Get()
        );

    // clear both the render target and depth stencil to default values
    const float ClearColor[4] = { 0.071f, 0.040f, 0.561f, 1.0f };
    m_d3dContext->ClearRenderTargetView(
        m_d3dRenderTargetView.Get(),
        ClearColor
        );

    m_d3dContext->ClearDepthStencilView(
        m_d3dDepthStencilView.Get(),
        D3D11_CLEAR_DEPTH,
        1.0f,
        0
        );

	cubeModel->Render(
		m_d3dContext.Get(),
		m_modelViewProjection,
		m_constantBuffer,
		m_sampler,

		modelTransform,

		shaderPairs,
		textures,
		shapes);
}